function myAjax(options) {   // type, url, data, timeout, success, error 封装成options
    // Step0: 将对象转换为字符串
    let str = objToString(options.data);

    // Step1: 创建一个异步对象
    let xhr, timer;    // 分情况： 1. IE 7+ Firefox Chrome Opera Safari 2. IE6 IE5
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();   // IE 7+ Firefox Chrome Opera Safari
    } else {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");    //IE6 IE5
    };

    /*
        异步对象常用方法：
            1. onreadystatechange
                监听异步对象请求状态码readyState的改变 每当readyState改变时，就会触发onreadystatechange事件
            2. readyState   请求状态码
                0：表示请求未初始化，还没有调用open()
                1：表示服务器连接已建立，但是还没有调用send()
                2：表示请求已经接受，正在处理中（通常现在可以从响应获取内容头）
                3：表示请求处理中，通常响应中已有部分数据可用，没有全部完成
                4：表示请求已经完成，此阶段确认全部数据都已经解析完毕，可以通过异步对象的属性获取对应的数据
            3. status    http状态码
                表示成功的状态码有 xhr.status >= 200 && xhr.status < 300 || xhr.status === 304
            4. responseText   后台返回的字符串形式的响应数据
            5. responseXML   后台返回的XML形式的响应数据
    */  

    // Step2: 设置请求方式和请求地址：xhr.open('GET/POST', url, true);
        /*
            1. 第一个参数：请求的类型，GET或者POST
            2. 第二个参数：请求的文件的地址，url
            3. 第三个参数：设置请求方式是同步还是异步 true异步 false异步
                AJAX存在的意义是发异步请求，所以第二个参数永远传true

            注意：IE中的缓存问题
                在IE浏览器中，如果通过Ajax发送【GET请求】，那么IE浏览器认为，同一个URL只有一个结果，
                如果地址没有发生变化，它就会把上一次返回的结果，直接返回。这样我们不能实时的拿到变化后的数据。
                如果要想我们拿到实时数据，必须保证每次的URL都是不一样的，有两种方式：
                    1. Math.random()
                    2. new Date().getTime()
                即在请求地址后面拼接上?t=随机数或者1970.01.01至当前的毫秒数
                所以在IE中通过ajax发送get请求时，可以设置请求地址为：
                xmlhttp.open("GET","ajax-get.txt?t=" + (new Date().getTime()),true);
                //或
                xmlhttp.open("GET","ajax-get.txt?t=" + Math.random(),true);
        */

    // Step3: 发送请求 xhr.send();
        /*
            注意：
                如果发送【POST请求】，请使用setRequestHeader()来添加http请求头，并在send()方法中传递要发送的数据
                添加的请求头为Content-type，目的是为了告诉服务器我们发送的数据类型，方便服务器去解析，如果不设置，那么服务端可能无法处理你的请求
                xhr.open('POST', url, true);
                xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        */

    // 判断请求类型
    if (options.type === 'GET') {
        xhr.open(options.type, options.url + '?t=' + str, true);
        xhr.send();
    }else {
        xhr.open(options.type, options.url + str, true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-form-urlencoded');
        xhr.send(str);
    };

    // Step4: 通过onreadystatechange监听状态变化
        /*
            每当异步对象的readyState发生改变，就会触发onreadystatechange函数
                1. 判断readyState === 4
                2. status >= 200 && status < 300 || status === 304
                    满足12表示请求成功
        */

    // Step5: 处理返回结果
        /*
            如果成功，可通过异步对象responseText属性来获取服务器返回的字符串
        */

        xhr.onreadystatechange = function() {
            clearInterval(timer);
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                    options.success(xhr);   // 成功后回调
                } else {
                    options.error(xhr); // 失败后回调
                };
            };
        };
    /*
        其他注意点：
            1. url中只能出现 字母 数字 下划线 和 ASCII码，不能出现中文 可以使用encodeURIComponent()转码
            2. ajax发送请求到远处服务器，我们需要等待远程服务器去响应，响应速度不稳定，需要新增功能：设置超时时间的功能
    */


    // 注意点1：转码问题
    function objToString(obj) {
        obj.t = new Date().getTime();
        var res = [];
        for (var key in obj) {
            res.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
        };
        return res.join('&');
    };


    // 注意点2：判断外界是否传入超时时间
    if (options.timeout) {
        timer = setInterval(function() {
            xhr.abort(); // 中断请求
            clearInterval(timer);
        }, options.timeout);
    };
};